package com.gd.iot.energy.service.common.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gd.iot.energy.code.ResponseCode;
import com.gd.iot.energy.consts.SystemConsts;
import com.gd.iot.energy.dao.superadmin.AdminPOMapper;
import com.gd.iot.energy.dao.superadmin.PermissionPOMapper;
import com.gd.iot.energy.dao.superadmin.RolePOMapper;
import com.gd.iot.energy.exception.MyException;
import com.gd.iot.energy.po.superadmin.AdminPO;
import com.gd.iot.energy.pojo.common.dto.*;
import com.gd.iot.energy.pojo.superadmin.vo.AdminVO;
import com.gd.iot.energy.pojo.common.vo.LoginResponseVO;
import com.gd.iot.energy.pojo.superadmin.dto.AdminDTO;
import com.gd.iot.energy.pojo.superadmin.dto.PermissionDTO;
import com.gd.iot.energy.pojo.superadmin.dto.RoleDTO;
import com.gd.iot.energy.service.common.CaptchaCacheService;
import com.gd.iot.energy.service.common.LoginService;
import com.gd.iot.energy.util.*;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 登录service实现类
 *
 * @author kris
 * @date Created at 2020/8/3 11:14
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Autowired
    private CaptchaCacheService captchaCacheService;

    @Resource
    private AdminPOMapper adminPOMapper;

    @Resource
    private RolePOMapper rolePOMapper;

    @Resource
    private PermissionPOMapper permissionPOMapper;

    @Value("${energy.maxLockTimes:5}")
    private Integer maxLockTimes;

    @Value("${energy.captcha.validate:true}")
    private Boolean validateCaptcha;

    @Override
    public LoginResponseVO login(PasswordLoginFormDTO passwordLoginFormDTO) {
        if (!passwordLoginFormDTO.validateNotEmpty()) {
            throw new MyException(ResponseCode.PARAMETER_REQUIRED);
        }

        //验证码
        if (validateCaptcha) {
            validateCaptcha(passwordLoginFormDTO.getCode(), passwordLoginFormDTO.getSessionId());
        }

        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                passwordLoginFormDTO.getUsername(),
                PasswordUtil.generateHmacSHA1Password(passwordLoginFormDTO.getPassword())
        );

        try {
            //进行验证，这里可以捕获异常，然后返回对应信息
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            addLockTime(passwordLoginFormDTO.getUsername());
            throw new MyException(ResponseCode.ADMIN_NOT_EXIST);
        } catch (LockedAccountException e) {
            addLockTime(passwordLoginFormDTO.getUsername());
            throw new MyException(ResponseCode.ADMIN_LOCKED);
        } catch (DisabledAccountException e) {
            addLockTime(passwordLoginFormDTO.getUsername());
            throw new MyException(ResponseCode.ADMIN_FORBIDDEN);
        } catch (IncorrectCredentialsException e) {
            addLockTime(passwordLoginFormDTO.getUsername());
            throw new MyException(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
        } catch (Exception e) {
            addLockTime(passwordLoginFormDTO.getUsername());
            throw new MyException(ResponseCode.LOGIN_FAILED);
        }
        AdminDTO adminDTO = (AdminDTO) subject.getPrincipal();
        adminDTO.setPassword(null);
        LoginResponseVO loginResponseDTO = new LoginResponseVO();

        //生成token
        AdminVO adminVO = new AdminVO();
        CustomBeanUtils.convert(adminDTO, adminVO);
        loginResponseDTO.setToken(subject.getSession().getId().toString());
        loginResponseDTO.setAdminVO(adminVO);

        return loginResponseDTO;
    }

    @Override
    public void updateLoginInfo(AdminDTO user) {
        AdminPO adminPO = new AdminPO();
        adminPO.setId(user.getId());
        adminPO.setLoginIp(CustomRequestUtil.getRequestIp());
        adminPO.setLoginDate(new Date());
        adminPOMapper.updateById(adminPO);
    }

    @Override
    public void unlock(AdminDTO user) {
        AdminPO adminPO = new AdminPO();
        adminPO.setId(user.getId());
        adminPO.setLoginFailureCount(0);
        adminPO.setIsLocked(SystemConsts.BooleanEnum.FALSE.value);
        adminPOMapper.updateById(adminPO);
    }

    @Override
    public void changePassword(ChangePasswordFormDTO changePasswordFormDTO) {
        if (StrUtil.isBlank(changePasswordFormDTO.getOldPassword())
                || StrUtil.isBlank(changePasswordFormDTO.getNewPassword())
                || StrUtil.isBlank(changePasswordFormDTO.getRePassword())) {
            throw new MyException(ResponseCode.PARAMETER_REQUIRED);
        }

        //2次输入密码对比
        if (!changePasswordFormDTO.getNewPassword().equals(changePasswordFormDTO.getRePassword())) {
            throw new MyException(ResponseCode.PASSWORD_NOT_SAME);
        }

        //邮箱合法性校验
        if (StrUtil.isNotBlank(changePasswordFormDTO.getEmail()) &&
                !CustomStrUtil.isEmail(changePasswordFormDTO.getEmail())) {
            throw new MyException(ResponseCode.EMAIL_FORMAT_ERROR);
        }

        AdminDTO userInfo = CurrentUserUtil.getUserInfo();
        AdminPO adminPO = adminPOMapper.selectById(userInfo.getId());
        if (adminPO == null) {
            throw new MyException(ResponseCode.RECORD_NOT_EXIST);
        }

        //验证原密码
        if (!PasswordUtil.validateHmacSHA1Password(changePasswordFormDTO.getOldPassword(), adminPO.getPassword())) {
            throw new MyException(ResponseCode.OLD_PASSWORD_ERROR);
        }

        //修改密码
        AdminPO adminPONew = new AdminPO();
        adminPONew.setPassword(PasswordUtil.generateHmacSHA1Password(changePasswordFormDTO.getNewPassword()));
        adminPONew.setId(adminPO.getId());
        adminPOMapper.updateById(adminPONew);
    }

    @Override
    public AdminVO getCurrentAdminInfo() {

        //session
        AdminDTO userInfo = CurrentUserUtil.getUserInfo();

        //数据库中
        AdminPO adminPO = adminPOMapper.selectById(userInfo.getId());

        //前端返回
        AdminVO adminVO = new AdminVO();
        CustomBeanUtils.convert(adminPO, adminVO);
        return adminVO;
    }

    /**
     * 增加失败次数
     *
     * @param username
     */
    private void addLockTime(String username) {

        QueryWrapper<AdminPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        queryWrapper.eq("is_del", SystemConsts.DeleteFlag.VALID.value);
        EnergyTypeUtil.injectEnergyTypeWrapper(queryWrapper);

        AdminPO adminPO = adminPOMapper.selectOne(queryWrapper);
        if (adminPO == null) {
            return;
        }
        AdminPO adminPOUpdate = new AdminPO();
        adminPOUpdate.setId(adminPO.getId());
        int failTimes = adminPO.getLoginFailureCount() + 1;
        if (failTimes > maxLockTimes) {
            if (!adminPO.getIsLocked()) {
                adminPOUpdate.setLockedDate(new Date());
            }
            adminPOUpdate.setIsLocked(SystemConsts.BooleanEnum.TRUE.value);

        }

        adminPOUpdate.setLoginFailureCount(failTimes);
        adminPOMapper.updateById(adminPOUpdate);
    }

    /**
     * 验证码
     *
     * @param code
     * @param sessionId
     */
    private void validateCaptcha(String code, String sessionId) {
        String captcha = captchaCacheService.get(sessionId);
        captchaCacheService.delete(sessionId);
        if (!code.equalsIgnoreCase(captcha)) {
            throw new MyException(ResponseCode.CAPTCHA_VALIDATE_FAILED);
        }
    }

    @Override
    public CaptchaDTO getCaptcha() {
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        String captchaKey = IdUtil.fastUUID();
        CaptchaDTO captchaDTO = new CaptchaDTO();
        try {
            //生产验证码字符串并保存到session中
            String createText = captchaProducer.createText();
            captchaCacheService.save(captchaKey, createText);

            //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
            BufferedImage challenge = captchaProducer.createImage(createText);

            ImageIO.write(challenge, "jpg", jpegOutputStream);
            String base64Image = CustomImageUtil.imageToBase64(jpegOutputStream.toByteArray());

            captchaDTO.setImage(base64Image);
            captchaDTO.setSessionId(captchaKey);
        } catch (IOException e) {
            throw new MyException(ResponseCode.CAPTCHA_GENERATE_ERROR);
        }

        return captchaDTO;
    }

    @Override
    public AdminDTO getAdminByName(String username) {
        //模拟数据库查询，正常情况此处是从数据库或者缓存查询。
        return getUserByName(username);
    }


    /**
     * 模拟数据库查询
     *
     * @param username
     * @return
     */
    private AdminDTO getUserByName(String username) {
        QueryWrapper<AdminPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        queryWrapper.eq("is_del", SystemConsts.DeleteFlag.VALID.value);
        EnergyTypeUtil.injectEnergyTypeWrapper(queryWrapper);

        AdminPO adminPO = adminPOMapper.selectOne(queryWrapper);
        if (adminPO == null) {
            return null;
        }
        AdminDTO adminDTO = new AdminDTO();
        CustomBeanUtils.convert(adminPO, adminDTO);

        //获取角色
        List<RoleDTO> roles = rolePOMapper.getRolesByAdminId(adminDTO.getId(), SystemConsts.EnergyTypeEnum.WATER.value);
        List<Integer> roleIds = roles.stream().map(RoleDTO::getId).collect(Collectors.toList());
        //获取权限
        List<PermissionDTO> permissionPOS = new ArrayList<>();
        if (!roleIds.isEmpty()) {
            permissionPOS = permissionPOMapper.getPermissionsByRoleId(roleIds, SystemConsts.EnergyTypeEnum.WATER.value);
        }
        for (RoleDTO roleDTO : roles) {
            List<PermissionDTO> permissionDTOList = permissionPOS.stream()
                    .filter(po -> po.getRoleId().equals(roleDTO.getId()))
                    .collect(Collectors.toList());
            roleDTO.setPermissionDTOS(permissionDTOList);
        }
        adminDTO.setRoles(roles);
        return adminDTO;
    }
}
